/* $XFree86: xc/programs/Xserver/dix/colormap.c,v 3.8 2001/12/14 19:59:29 dawes Exp $ */
/***********************************************************

Copyright 1987, 1998  The Open Group

Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of The Open Group shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from The Open Group.


Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the name of Digital not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/

/* $Xorg: colormap.c,v 1.4 2001/02/09 02:04:39 xorgcvs Exp $ */

#include <X11/X.h>
#define NEED_EVENTS
#include <X11/Xproto.h>
#include "misc.h"
#include "dix.h"
#include "colormapst.h"
#include "os.h"
#include "scrnintstr.h"
#include "resource.h"
#include "windowstr.h"
#ifdef LBX
#include "lbxserve.h"
#endif

extern XID clientErrorValue;

static Pixel FindBestPixel(
#if NeedFunctionPrototypes
    EntryPtr /*pentFirst*/,
    int /*size*/,
    xrgb * /*prgb*/,
    int /*channel*/
#endif
);

static int AllComp(
#if NeedFunctionPrototypes
    EntryPtr /*pent*/,
    xrgb * /*prgb*/
#endif
);

static int RedComp(
#if NeedFunctionPrototypes
    EntryPtr /*pent*/,
    xrgb * /*prgb*/
#endif
);

static int GreenComp(
#if NeedFunctionPrototypes
    EntryPtr /*pent*/,
    xrgb * /*prgb*/
#endif
);

static int BlueComp(
#if NeedFunctionPrototypes
    EntryPtr /*pent*/,
    xrgb * /*prgb*/
#endif
);

static void FreePixels(
#if NeedFunctionPrototypes
    register ColormapPtr /*pmap*/,
    register int /*client*/
#endif
);

static void CopyFree(
#if NeedFunctionPrototypes
    int /*channel*/,
    int /*client*/,
    ColormapPtr /*pmapSrc*/,
    ColormapPtr /*pmapDst*/
#endif
);

static void FreeCell(
#if NeedFunctionPrototypes
    ColormapPtr /*pmap*/,
    Pixel /*i*/,
    int /*channel*/
#endif
);

static void UpdateColors(
#if NeedFunctionPrototypes
    ColormapPtr /*pmap*/
#endif
);

static int AllocDirect(
#if NeedFunctionPrototypes
    int /*client*/,
    ColormapPtr /*pmap*/,
    int /*c*/,
    int /*r*/,
    int /*g*/,
    int /*b*/,
    Bool /*contig*/,
    Pixel * /*pixels*/,
    Pixel * /*prmask*/,
    Pixel * /*pgmask*/,
    Pixel * /*pbmask*/
#endif
);

static int AllocPseudo(
#if NeedFunctionPrototypes
    int /*client*/,
    ColormapPtr /*pmap*/,
    int /*c*/,
    int /*r*/,
    Bool /*contig*/,
    Pixel * /*pixels*/,
    Pixel * /*pmask*/,
    Pixel ** /*pppixFirst*/
#endif
);

static Bool AllocCP(
#if NeedFunctionPrototypes
    ColormapPtr /*pmap*/,
    EntryPtr /*pentFirst*/,
    int /*count*/,
    int /*planes*/,
    Bool /*contig*/,
    Pixel * /*pixels*/,
    Pixel * /*pMask*/
#endif
);

static Bool AllocShared(
#if NeedFunctionPrototypes
    ColormapPtr /*pmap*/,
    Pixel * /*ppix*/,
    int /*c*/,
    int /*r*/,
    int /*g*/,
    int /*b*/,
    Pixel /*rmask*/,
    Pixel /*gmask*/,
    Pixel /*bmask*/,
    Pixel * /*ppixFirst*/
#endif
);

static int FreeCo(
#if NeedFunctionPrototypes
    ColormapPtr /*pmap*/,
    int /*client*/,
    int /*color*/,
    int /*npixIn*/,
    Pixel * /*ppixIn*/,
    Pixel /*mask*/
#endif
);

static int   TellNoMap(
#if NeedFunctionPrototypes
    WindowPtr	/*pwin*/,
    Colormap 	* /*pmid*/
#endif
);

static void FindColorInRootCmap (
#if NeedFunctionPrototypes
    ColormapPtr	/* pmap */,
    EntryPtr	/* pentFirst */,
    int		/* size */,
    xrgb*	/* prgb */,
    Pixel*	/* pPixel */,
    int		/* channel */,
    ColorCompareProcPtr /* comp */
#endif
);

#define NUMRED(vis) ((vis->redMask >> vis->offsetRed) + 1)
#define NUMGREEN(vis) ((vis->greenMask >> vis->offsetGreen) + 1)
#define NUMBLUE(vis) ((vis->blueMask >> vis->offsetBlue) + 1)
#define RGBMASK(vis) (vis->redMask | vis->greenMask | vis->blueMask)

/* GetNextBitsOrBreak(bits, mask, base)  -- 
 * (Suggestion: First read the macro, then read this explanation.
 *
 * Either generate the next value to OR in to a pixel or break out of this
 * while loop 
 *
 * This macro is used when we're trying to generate all 2^n combinations of
 * bits in mask.  What we're doing here is counting in binary, except that
 * the bits we use to count may not be contiguous.  This macro will be
 * called 2^n times, returning a different value in bits each time. Then
 * it will cause us to break out of a surrounding loop. (It will always be
 * called from within a while loop.)
 * On call: mask is the value we want to find all the combinations for
 * base has 1 bit set where the least significant bit of mask is set
 *
 * For example,if mask is 01010, base should be 0010 and we count like this:
 * 00010 (see this isn't so hard), 
 *     then we add base to bits and get 0100. (bits & ~mask) is (0100 & 0100) so
 *      we add that to bits getting (0100 + 0100) =
 * 01000 for our next value.
 *      then we add 0010 to get 
 * 01010 and we're done (easy as 1, 2, 3)
 */
#define GetNextBitsOrBreak(bits, mask, base)	\
	    if((bits) == (mask)) 		\
		break;		 		\
	    (bits) += (base);		 	\
	    while((bits) & ~(mask))		\
		(bits) += ((bits) & ~(mask));	
/* ID of server as client */
#define SERVER_ID	0

typedef struct _colorResource
{
	Colormap	mid;
	int		client;
} colorResource;

/* Invariants:
 * refcnt == 0 means entry is empty
 * refcnt > 0 means entry is useable by many clients, so it can't be changed
 * refcnt == AllocPrivate means entry owned by one client only
 * fShared should only be set if refcnt == AllocPrivate, and only in red map
 */


/* Create and initialize the color map */
int 
CreateColormap (mid, pScreen, pVisual, ppcmap, alloc, client)
    Colormap	mid;		/* resource to use for this colormap */
    ScreenPtr	pScreen;
    VisualPtr	pVisual;
    ColormapPtr	*ppcmap;	
    int		alloc;		/* 1 iff all entries are allocated writeable */
    int		client;
{
    int		class, size;
    unsigned long sizebytes;
    ColormapPtr	pmap;
    register	EntryPtr	pent;
    int		i;
    register	Pixel	*ppix, **pptr;
    extern int colormapPrivateCount;

    class = pVisual->class;
    if(!(class & DynamicClass) && (alloc != AllocNone) && (client != SERVER_ID))
	return (BadMatch);

    size = pVisual->ColormapEntries;
    sizebytes = (size * sizeof(Entry)) +
		(MAXCLIENTS * sizeof(Pixel *)) +
		(MAXCLIENTS * sizeof(int));
    if ((class | DynamicClass) == DirectColor)
	sizebytes *= 3;
    sizebytes += sizeof(ColormapRec);
    pmap = (ColormapPtr) xalloc(sizebytes);
    if (!pmap)
	return (BadAlloc);
    pmap->red = (EntryPtr)((char *)pmap + sizeof(ColormapRec));    
    sizebytes = size * sizeof(Entry);
    pmap->clientPixelsRed = (Pixel **)((char *)pmap->red + sizebytes);
    pmap->numPixelsRed = (int *)((char *)pmap->clientPixelsRed +
				 (MAXCLIENTS * sizeof(Pixel *)));
    pmap->mid = mid;
    pmap->flags = 0; 	/* start out with all flags clear */
    if(mid == pScreen->defColormap)
	pmap->flags |= IsDefault;
    pmap->pScreen = pScreen;
    pmap->pVisual = pVisual;
    pmap->class = class;
    if ((class | DynamicClass) == DirectColor)
	size = NUMRED(pVisual);
    pmap->freeRed = size;
    bzero ((char *) pmap->red, (int)sizebytes);
    bzero((char *) pmap->numPixelsRed, MAXCLIENTS * sizeof(int));
    for (pptr = &pmap->clientPixelsRed[MAXCLIENTS]; --pptr >= pmap->clientPixelsRed; )
	*pptr = (Pixel *)NULL;
    if (alloc == AllocAll)
    {
	if (class & DynamicClass)
	    pmap->flags |= AllAllocated;
	for (pent = &pmap->red[size - 1]; pent >= pmap->red; pent--)
	    pent->refcnt = AllocPrivate;
	pmap->freeRed = 0;
	ppix = (Pixel *)xalloc(size * sizeof(Pixel));
	if (!ppix)
	{
	    xfree(pmap);
	    return (BadAlloc);
	}
	pmap->clientPixelsRed[client] = ppix;
	for(i = 0; i < size; i++)
	    ppix[i] = i;
	pmap->numPixelsRed[client] = size;
    }

    if ((class | DynamicClass) == DirectColor)
    {
	pmap->freeGreen = NUMGREEN(pVisual);
	pmap->green = (EntryPtr)((char *)pmap->numPixelsRed +
				 (MAXCLIENTS * sizeof(int)));
	pmap->clientPixelsGreen = (Pixel **)((char *)pmap->green + sizebytes);
	pmap->numPixelsGreen = (int *)((char *)pmap->clientPixelsGreen +
				       (MAXCLIENTS * sizeof(Pixel *)));
	pmap->freeBlue = NUMBLUE(pVisual);
	pmap->blue = (EntryPtr)((char *)pmap->numPixelsGreen +
				(MAXCLIENTS * sizeof(int)));
	pmap->clientPixelsBlue = (Pixel **)((char *)pmap->blue + sizebytes);
	pmap->numPixelsBlue = (int *)((char *)pmap->clientPixelsBlue +
				      (MAXCLIENTS * sizeof(Pixel *)));

	bzero ((char *) pmap->green, (int)sizebytes);
	bzero ((char *) pmap->blue, (int)sizebytes);

	memmove((char *) pmap->clientPixelsGreen,
		(char *) pmap->clientPixelsRed,
	      MAXCLIENTS * sizeof(Pixel *));
	memmove((char *) pmap->clientPixelsBlue,
		(char *) pmap->clientPixelsRed,
	      MAXCLIENTS * sizeof(Pixel *));
	bzero((char *) pmap->numPixelsGreen, MAXCLIENTS * sizeof(int));
	bzero((char *) pmap->numPixelsBlue, MAXCLIENTS * sizeof(int));

	/* If every cell is allocated, mark its refcnt */
	if (alloc == AllocAll)
	{
	    size = pmap->freeGreen;
	    for(pent = &pmap->green[size-1]; pent >= pmap->green; pent--)
		pent->refcnt = AllocPrivate;
	    pmap->freeGreen = 0;
	    ppix = (Pixel *) xalloc(size * sizeof(Pixel));
	    if (!ppix)
	    {
		xfree(pmap->clientPixelsRed[client]);
		xfree(pmap);
		return(BadAlloc);
	    }
	    pmap->clientPixelsGreen[client] = ppix;
	    for(i = 0; i < size; i++)
		ppix[i] = i;
	    pmap->numPixelsGreen[client] = size;

	    size = pmap->freeBlue;
	    for(pent = &pmap->blue[size-1]; pent >= pmap->blue; pent--)
		pent->refcnt = AllocPrivate;
	    pmap->freeBlue = 0;
	    ppix = (Pixel *) xalloc(size * sizeof(Pixel));
	    if (!ppix)
	    {
		xfree(pmap->clientPixelsGreen[client]);
		xfree(pmap->clientPixelsRed[client]);
		xfree(pmap);
		return(BadAlloc);
	    }
	    pmap->clientPixelsBlue[client] = ppix;
	    for(i = 0; i < size; i++)
		ppix[i] = i;
	    pmap->numPixelsBlue[client] = size;
	}
    }
    if (!AddResource(mid, RT_COLORMAP, (pointer)pmap))
	return (BadAlloc);
    /* If the device wants a chance to initialize the colormap in any way,
     * this is it.  In specific, if this is a Static colormap, this is the
     * time to fill in the colormap's values */
    pmap->flags |= BeingCreated;


    /*
     * Allocate the array of devPrivate's for this colormap.
     */

    if (colormapPrivateCount == 0)
	pmap->devPrivates = NULL;
    else
    {
	pmap->devPrivates = (DevUnion *) xalloc (
	    colormapPrivateCount * sizeof(DevUnion));

	if (!pmap->devPrivates)
	{
	    FreeResource (mid, RT_NONE);
	    return BadAlloc;
	}
    }

    if (!(*pScreen->CreateColormap)(pmap))
    {
	FreeResource (mid, RT_NONE);
	return BadAlloc;
    }
    pmap->flags &= ~BeingCreated;
    *ppcmap = pmap;
    return (Success);
}

int
FreeColormap (value, mid)
    pointer	value; /* must conform to DeleteType */
    XID		mid;
{
    int		i;
    register EntryPtr pent;
    ColormapPtr	pmap = (ColormapPtr)value;

    if(CLIENT_ID(mid) != SERVER_ID)
    {
        (*pmap->pScreen->UninstallColormap) (pmap);
        WalkTree(pmap->pScreen, (VisitWindowProcPtr)TellNoMap, (pointer) &mid);
    }

    /* This is the device's chance to undo anything it needs to, especially
     * to free any storage it allocated */
    (*pmap->pScreen->DestroyColormap)(pmap);

    if(pmap->clientPixelsRed)
    {
	for(i = 0; i < MAXCLIENTS; i++)
	    xfree(pmap->clientPixelsRed[i]);
    }

    if ((pmap->class == PseudoColor) || (pmap->class == GrayScale))
    {
	for(pent = &pmap->red[pmap->pVisual->ColormapEntries - 1];
	    pent >= pmap->red;
	    pent--)
	{
	    if(pent->fShared)
	    {
		if (--pent->co.shco.red->refcnt == 0)
		    xfree(pent->co.shco.red);
		if (--pent->co.shco.green->refcnt == 0)
		    xfree(pent->co.shco.green);
		if (--pent->co.shco.blue->refcnt == 0)
		    xfree(pent->co.shco.blue);
	    }
	}
    }
    if((pmap->class | DynamicClass) == DirectColor)
    {
        for(i = 0; i < MAXCLIENTS; i++)
	{
            xfree(pmap->clientPixelsGreen[i]);
            xfree(pmap->clientPixelsBlue[i]);
        }
    }

    if (pmap->devPrivates)
	xfree(pmap->devPrivates);

    xfree(pmap);
    return(Success);
}

/* Tell window that pmid has disappeared */
static int
TellNoMap (pwin, pmid)
    WindowPtr	pwin;
    Colormap 	*pmid;
{
    xEvent 	xE;

#ifdef PANORAMIX
    if(!noPanoramiXExtension && pwin->drawable.pScreen->myNum)
	return WT_STOPWALKING;
#endif
    if (wColormap(pwin) == *pmid)
    {
	/* This should be call to DeliverEvent */
	xE.u.u.type = ColormapNotify;
	xE.u.colormap.window = pwin->drawable.id;
	xE.u.colormap.colormap = None;
	xE.u.colormap.new = TRUE;
	xE.u.colormap.state = ColormapUninstalled;
	DeliverEvents(pwin, &xE, 1, (WindowPtr)NULL);
	if (pwin->optional) {
	    pwin->optional->colormap = None;
	    CheckWindowOptionalNeed (pwin);
	}
    }

    return (WT_WALKCHILDREN);
}

/* Tell window that pmid got uninstalled */
int
TellLostMap (pwin, value)
    WindowPtr	pwin;
    pointer	value;
{
    Colormap 	*pmid = (Colormap *)value;
    xEvent 	xE;

#ifdef PANORAMIX
    if(!noPanoramiXExtension && pwin->drawable.pScreen->myNum)
	return WT_STOPWALKING;
#endif
    if (wColormap(pwin) == *pmid)
    {
	/* This should be call to DeliverEvent */
	xE.u.u.type = ColormapNotify;
	xE.u.colormap.window = pwin->drawable.id;
	xE.u.colormap.colormap = *pmid;
	xE.u.colormap.new = FALSE;
	xE.u.colormap.state = ColormapUninstalled;
	DeliverEvents(pwin, &xE, 1, (WindowPtr)NULL);
    }

    return (WT_WALKCHILDREN);
}

/* Tell window that pmid got installed */
int
TellGainedMap (pwin, value)
    WindowPtr	pwin;
    pointer	value;
{
    Colormap 	*pmid = (Colormap *)value;
    xEvent 	xE;

#ifdef PANORAMIX
    if(!noPanoramiXExtension && pwin->drawable.pScreen->myNum)
	return WT_STOPWALKING;
#endif
    if (wColormap (pwin) == *pmid)
    {
	/* This should be call to DeliverEvent */
	xE.u.u.type = ColormapNotify;
	xE.u.colormap.window = pwin->drawable.id;
	xE.u.colormap.colormap = *pmid;
	xE.u.colormap.new = FALSE;
	xE.u.colormap.state = ColormapInstalled;
	DeliverEvents(pwin, &xE, 1, (WindowPtr)NULL);
    }

    return (WT_WALKCHILDREN);
}

  
int
CopyColormapAndFree (mid, pSrc, client)
    Colormap	mid;
    ColormapPtr	pSrc;
    int		client;
{
    ColormapPtr	pmap = (ColormapPtr) NULL;
    int		result, alloc, size;
    Colormap	midSrc;
    ScreenPtr	pScreen;
    VisualPtr	pVisual;

    pScreen = pSrc->pScreen;
    pVisual = pSrc->pVisual;
    midSrc = pSrc->mid;
    alloc = ((pSrc->flags & AllAllocated) && CLIENT_ID(midSrc) == client) ?
            AllocAll : AllocNone;
    size = pVisual->ColormapEntries;

    /* If the create returns non-0, it failed */
    result = CreateColormap (mid, pScreen, pVisual, &pmap, alloc, client);
    if(result != Success)
        return(result);
    if(alloc == AllocAll)
    {
	memmove((char *)pmap->red, (char *)pSrc->red, size * sizeof(Entry));
	if((pmap->class | DynamicClass) == DirectColor)
	{
	    memmove((char *)pmap->green, (char *)pSrc->green, size * sizeof(Entry));
	    memmove((char *)pmap->blue, (char *)pSrc->blue, size * sizeof(Entry));
	}
	pSrc->flags &= ~AllAllocated;
	FreePixels(pSrc, client);
	UpdateColors(pmap);
	return(Success);
    }

    CopyFree(REDMAP, client, pSrc, pmap);
    if ((pmap->class | DynamicClass) == DirectColor)
    {
	CopyFree(GREENMAP, client, pSrc, pmap);
	CopyFree(BLUEMAP, client, pSrc, pmap);
    }
    if (pmap->class & DynamicClass)
	UpdateColors(pmap);
    /* XXX should worry about removing any RT_CMAPENTRY resource */
    return(Success);
}

/* Helper routine for freeing large numbers of cells from a map */
static void
CopyFree (channel, client, pmapSrc, pmapDst)
    int		channel, client;
    ColormapPtr	pmapSrc, pmapDst;
{
    int		z, npix, oldFree;
    EntryPtr	pentSrcFirst, pentDstFirst;
    EntryPtr	pentSrc, pentDst;
    Pixel	*ppix;
    int		nalloc;

    switch(channel)
    {
      default:	/* so compiler can see that everything gets initialized */
      case REDMAP:
	ppix = (pmapSrc->clientPixelsRed)[client];
	npix = (pmapSrc->numPixelsRed)[client];
	pentSrcFirst = pmapSrc->red;
	pentDstFirst = pmapDst->red;
	oldFree = pmapSrc->freeRed;
	break;
      case GREENMAP:
	ppix = (pmapSrc->clientPixelsGreen)[client];
	npix = (pmapSrc->numPixelsGreen)[client];
	pentSrcFirst = pmapSrc->green;
	pentDstFirst = pmapDst->green;
	oldFree = pmapSrc->freeGreen;
	break;
      case BLUEMAP:
	ppix = (pmapSrc->clientPixelsBlue)[client];
	npix = (pmapSrc->numPixelsBlue)[client];
	pentSrcFirst = pmapSrc->blue;
	pentDstFirst = pmapDst->blue;
	oldFree = pmapSrc->freeBlue;
	break;
    }
    nalloc = 0;
    if (pmapSrc->class & DynamicClass)
    {
	for(z = npix; --z >= 0; ppix++)
	{
	    /* Copy entries */
	    pentSrc = pentSrcFirst + *ppix;
	    pentDst = pentDstFirst + *ppix;
	    if (pentDst->refcnt > 0)
	    {
		pentDst->refcnt++;
	    }
	    else
	    {
		*pentDst = *pentSrc;
		nalloc++;
		if (pentSrc->refcnt > 0)
		    pentDst->refcnt = 1;
		else
		    pentSrc->fShared = FALSE;
	    }
	    FreeCell(pmapSrc, *ppix, channel);
	}
    }

    /* Note that FreeCell has already fixed pmapSrc->free{Color} */
    switch(channel)
    {
      case REDMAP:
        pmapDst->freeRed -= nalloc;
        (pmapDst->clientPixelsRed)[client] =
	    (pmapSrc->clientPixelsRed)[client];
        (pmapSrc->clientPixelsRed)[client] = (Pixel *) NULL;
        (pmapDst->numPixelsRed)[client] = (pmapSrc->numPixelsRed)[client];
        (pmapSrc->numPixelsRed)[client] = 0;
	break;
      case GREENMAP:
        pmapDst->freeGreen -= nalloc;
        (pmapDst->clientPixelsGreen)[client] =
	    (pmapSrc->clientPixelsGreen)[client];
        (pmapSrc->clientPixelsGreen)[client] = (Pixel *) NULL;
        (pmapDst->numPixelsGreen)[client] = (pmapSrc->numPixelsGreen)[client];
        (pmapSrc->numPixelsGreen)[client] = 0;
	break;
      case BLUEMAP:
        pmapDst->freeBlue -= nalloc;
        pmapDst->clientPixelsBlue[client] = pmapSrc->clientPixelsBlue[client];
        pmapSrc->clientPixelsBlue[client] = (Pixel *) NULL;
        pmapDst->numPixelsBlue[client] = pmapSrc->numPixelsBlue[client];
        pmapSrc->numPixelsBlue[client] = 0;
	break;
    }
}

/* Free the ith entry in a color map.  Must handle freeing of
 * colors allocated through AllocColorPlanes */
static void
FreeCell (pmap, i, channel)
    ColormapPtr pmap;
    Pixel i;
    int	channel;
{
    EntryPtr pent;
    int	*pCount;


    switch (channel)
    {
      default:	/* so compiler can see that everything gets initialized */
      case PSEUDOMAP:
      case REDMAP:
          pent = (EntryPtr) &pmap->red[i];
	  pCount = &pmap->freeRed;
	  break;
      case GREENMAP:
          pent = (EntryPtr) &pmap->green[i];
	  pCount = &pmap->freeGreen;
	  break;
      case BLUEMAP:
          pent = (EntryPtr) &pmap->blue[i];
	  pCount = &pmap->freeBlue;
	  break;
    }
    /* If it's not privately allocated and it's not time to free it, just
     * decrement the count */
    if (pent->refcnt > 1)
	pent->refcnt--;
    else
    {
        /* If the color type is shared, find the sharedcolor. If decremented
         * refcnt is 0, free the shared cell. */
        if (pent->fShared)
	{
	    if(--pent->co.shco.red->refcnt == 0)
		xfree(pent->co.shco.red);
	    if(--pent->co.shco.green->refcnt == 0)
		xfree(pent->co.shco.green);
	    if(--pent->co.shco.blue->refcnt == 0)
		xfree(pent->co.shco.blue);
	    pent->fShared = FALSE;
	}
	pent->refcnt = 0;
	*pCount += 1;
    }
}

static void
UpdateColors (pmap)
    ColormapPtr	pmap;
{
    xColorItem		*defs;
    register xColorItem *pdef;
    register EntryPtr 	pent;
    register VisualPtr	pVisual;
    int			i, n, size;

    pVisual = pmap->pVisual;
    size = pVisual->ColormapEntries;
    defs = (xColorItem *)ALLOCATE_LOCAL(size * sizeof(xColorItem));
    if (!defs)
	return;
    n = 0;
    pdef = defs;
    if (pmap->class == DirectColor)
    {
        for (i = 0; i < size; i++)
	{
	    if (!pmap->red[i].refcnt &&
		!pmap->green[i].refcnt &&
		!pmap->blue[i].refcnt)
		continue;
	    pdef->pixel = ((Pixel)i << pVisual->offsetRed) |
			  ((Pixel)i << pVisual->offsetGreen) |
			  ((Pixel)i << pVisual->offsetBlue);
	    pdef->red = pmap->red[i].co.local.red;
	    pdef->green = pmap->green[i].co.local.green;
	    pdef->blue = pmap->blue[i].co.local.blue;
	    pdef->flags = DoRed|DoGreen|DoBlue;
	    pdef++;
	    n++;
	}
    }
    else
    {
        for (i = 0, pent = pmap->red; i < size; i++, pent++)
	{
	    if (!pent->refcnt)
		continue;
	    pdef->pixel = i;
	    if(pent->fShared)
	    {
		pdef->red = pent->co.shco.red->color;
		pdef->green = pent->co.shco.green->color;
		pdef->blue = pent->co.shco.blue->color;
	    }
	    else
	    {
		pdef->red = pent->co.local.red;
		pdef->green = pent->co.local.green;
		pdef->blue = pent->co.local.blue;
	    }
	    pdef->flags = DoRed|DoGreen|DoBlue;
	    pdef++;
	    n++;
	}
    }
    if (n)
	(*pmap->pScreen->StoreColors)(pmap, n, defs);
    DEALLOCATE_LOCAL(defs);
}

/* Get a read-only color from a ColorMap (probably slow for large maps)
 * Returns by changing the value in pred, pgreen, pblue and pPix
 */
int
AllocColor (pmap, pred, pgreen, pblue, pPix, client)
    ColormapPtr		pmap;
    unsigned short 	*pred, *pgreen, *pblue;
    Pixel		*pPix;
    int			client;
{
    Pixel	pixR, pixG, pixB;
    int		entries;
    xrgb	rgb;
    int		class;
    VisualPtr	pVisual;
    int		npix;
    Pixel	*ppix;

    pVisual = pmap->pVisual;
    (*pmap->pScreen->ResolveColor) (pred, pgreen, pblue, pVisual);
    rgb.red = *pred;
    rgb.green = *pgreen;
    rgb.blue = *pblue;
    class = pmap->class;
    entries = pVisual->ColormapEntries;

    /* If the colormap is being created, then we want to be able to change
     * the colormap, even if it's a static type. Otherwise, we'd never be
     * able to initialize static colormaps
     */
    if(pmap->flags & BeingCreated)
	class |= DynamicClass;

    /* If this is one of the static storage classes, and we're not initializing
     * it, the best we can do is to find the closest color entry to the
     * requested one and return that.
     */
    switch (class) {
    case StaticColor:
    case StaticGray:
	/* Look up all three components in the same pmap */
	*pPix = pixR = FindBestPixel(pmap->red, entries, &rgb, PSEUDOMAP);
	*pred = pmap->red[pixR].co.local.red;
	*pgreen = pmap->red[pixR].co.local.green;
	*pblue = pmap->red[pixR].co.local.blue;
	npix = pmap->numPixelsRed[client];
	ppix = (Pixel *) xrealloc(pmap->clientPixelsRed[client],
				  (npix + 1) * sizeof(Pixel));
	if (!ppix)
	    return (BadAlloc);
	ppix[npix] = pixR;
	pmap->clientPixelsRed[client] = ppix;
	pmap->numPixelsRed[client]++;
	break;

    case TrueColor:
	/* Look up each component in its own map, then OR them together */
	pixR = FindBestPixel(pmap->red, NUMRED(pVisual), &rgb, REDMAP);
	pixG = FindBestPixel(pmap->green, NUMGREEN(pVisual), &rgb, GREENMAP);
	pixB = FindBestPixel(pmap->blue, NUMBLUE(pVisual), &rgb, BLUEMAP);
	*pPix = (pixR << pVisual->offsetRed) |
		(pixG << pVisual->offsetGreen) |
		(pixB << pVisual->offsetBlue);
	*pred = pmap->red[pixR].co.local.red;
	*pgreen = pmap->green[pixG].co.local.green;
	*pblue = pmap->blue[pixB].co.local.blue;
	npix = pmap->numPixelsRed[client];
	ppix = (Pixel *) xrealloc(pmap->clientPixelsRed[client],
				  (npix + 1) * sizeof(Pixel));
	if (!ppix)
	    return (BadAlloc);
	ppix[npix] = pixR;
	pmap->clientPixelsRed[client] = ppix;
	npix = pmap->numPixelsGreen[client];
	ppix = (Pixel *) xrealloc(pmap->clientPixelsGreen[client],
				  (npix + 1) * sizeof(Pixel));
	if (!ppix)
	    return (BadAlloc);
	ppix[npix] = pixG;
	pmap->clientPixelsGreen[client] = ppix;
	npix = pmap->numPixelsBlue[client];
	ppix = (Pixel *) xrealloc(pmap->clientPixelsBlue[client],
				  (npix + 1) * sizeof(Pixel));
	if (!ppix)
	    return (BadAlloc);
	ppix[npix] = pixB;
	pmap->clientPixelsBlue[client] = ppix;
	pmap->numPixelsRed[client]++;
	pmap->numPixelsGreen[client]++;
	pmap->numPixelsBlue[client]++;
	break;

    case GrayScale:
    case PseudoColor:
	if (pmap->mid != pmap->pScreen->defColormap &&
	    pmap->pVisual->vid == pmap->pScreen->rootVisual)
	{
	    ColormapPtr prootmap = (ColormapPtr)
		SecurityLookupIDByType (clients[client], pmap->pScreen->defColormap,
					 RT_COLORMAP, SecurityReadAccess);

	    if (pmap->class == prootmap->class)
		FindColorInRootCmap (prootmap, prootmap->red, entries, &rgb, 
			pPix, PSEUDOMAP, AllComp);
	}
	if (FindColor(pmap, pmap->red, entries, &rgb, pPix, PSEUDOMAP,
		      client, AllComp) != Success)
	    return (BadAlloc);
        break;

    case DirectColor:
	if (pmap->mid != pmap->pScreen->defColormap &&
	    pmap->pVisual->vid == pmap->pScreen->rootVisual)
	{
	    ColormapPtr prootmap = (ColormapPtr)
		SecurityLookupIDByType (clients[client], pmap->pScreen->defColormap,
					 RT_COLORMAP, SecurityReadAccess);

	    if (pmap->class == prootmap->class)
	    {
		pixR = (*pPix & pVisual->redMask) >> pVisual->offsetRed; 
		FindColorInRootCmap (prootmap, prootmap->red, entries, &rgb, 
			&pixR, REDMAP, RedComp);
		pixG = (*pPix & pVisual->greenMask) >> pVisual->offsetGreen; 
		FindColorInRootCmap (prootmap, prootmap->green, entries, &rgb, 
			&pixG, GREENMAP, GreenComp);
		pixB = (*pPix & pVisual->blueMask) >> pVisual->offsetBlue; 
		FindColorInRootCmap (prootmap, prootmap->blue, entries, &rgb, 
			&pixB, BLUEMAP, BlueComp);
		*pPix = pixR | pixG | pixB;
	    }
	}

	pixR = (*pPix & pVisual->redMask) >> pVisual->offsetRed; 
	if (FindColor(pmap, pmap->red, NUMRED(pVisual), &rgb, &pixR, REDMAP,
		      client, RedComp) != Success)
	    return (BadAlloc);
	pixG = (*pPix & pVisual->greenMask) >> pVisual->offsetGreen; 
	if (FindColor(pmap, pmap->green, NUMGREEN(pVisual), &rgb, &pixG,
		      GREENMAP, client, GreenComp) != Success)
	{
	    (void)FreeCo(pmap, client, REDMAP, 1, &pixR, (Pixel)0);
	    return (BadAlloc);
	}
	pixB = (*pPix & pVisual->blueMask) >> pVisual->offsetBlue; 
	if (FindColor(pmap, pmap->blue, NUMBLUE(pVisual), &rgb, &pixB, BLUEMAP,
		      client, BlueComp) != Success)
	{
	    (void)FreeCo(pmap, client, GREENMAP, 1, &pixG, (Pixel)0);
	    (void)FreeCo(pmap, client, REDMAP, 1, &pixR, (Pixel)0);
	    return (BadAlloc);
	}
	*pPix = pixR | pixG | pixB;
	break;
    }

    /* if this is the client's first pixel in this colormap, tell the
     * resource manager that the client has pixels in this colormap which
     * should be freed when the client dies */
    if ((pmap->numPixelsRed[client] == 1) &&
	(CLIENT_ID(pmap->mid) != client) &&
	!(pmap->flags & BeingCreated))
    {
	colorResource	*pcr;

	pcr = (colorResource *) xalloc(sizeof(colorResource));
	if (!pcr)
	{
	    (void)FreeColors(pmap, client, 1, pPix, (Pixel)0);
	    return (BadAlloc);
	}
	pcr->mid = pmap->mid;
	pcr->client = client;
	if (!AddResource(FakeClientID(client), RT_CMAPENTRY, (pointer)pcr))
	    return (BadAlloc);
    }
    return (Success);
}

/*
 * FakeAllocColor -- fake an AllocColor request by
 * returning a free pixel if availible, otherwise returning
 * the closest matching pixel.  This is used by the mi
 * software sprite code to recolor cursors.  A nice side-effect
 * is that this routine will never return failure.
 */

void
FakeAllocColor (pmap, item)
    register ColormapPtr pmap;
    register xColorItem  *item;
{
    Pixel	pixR, pixG, pixB;
    Pixel	temp;
    int		entries;
    xrgb	rgb;
    int		class;
    register VisualPtr	pVisual;

    pVisual = pmap->pVisual;
    rgb.red = item->red;
    rgb.green = item->green;
    rgb.blue = item->blue;
    (*pmap->pScreen->ResolveColor) (&rgb.red, &rgb.green, &rgb.blue, pVisual);
    class = pmap->class;
    entries = pVisual->ColormapEntries;

    switch (class) {
    case GrayScale:
    case PseudoColor:
	item->pixel = 0;
	if (FindColor(pmap, pmap->red, entries, &rgb, &temp, PSEUDOMAP,
		      -1, AllComp) == Success) {
	    item->pixel = temp;
	    break;
	}
	/* fall through ... */
    case StaticColor:
    case StaticGray:
	item->pixel = FindBestPixel(pmap->red, entries, &rgb, PSEUDOMAP);
	break;

    case DirectColor:
	/* Look up each component in its own map, then OR them together */
	pixR = (item->pixel & pVisual->redMask) >> pVisual->offsetRed; 
	pixG = (item->pixel & pVisual->greenMask) >> pVisual->offsetGreen; 
	pixB = (item->pixel & pVisual->blueMask) >> pVisual->offsetBlue; 
	if (FindColor(pmap, pmap->red, NUMRED(pVisual), &rgb, &pixR, REDMAP,
		      -1, RedComp) != Success)
	    pixR = FindBestPixel(pmap->red, NUMRED(pVisual), &rgb, REDMAP)
			<< pVisual->offsetRed;
	if (FindColor(pmap, pmap->green, NUMGREEN(pVisual), &rgb, &pixG,
		      GREENMAP, -1, GreenComp) != Success)
	    pixG = FindBestPixel(pmap->green, NUMGREEN(pVisual), &rgb,
				 GREENMAP) << pVisual->offsetGreen;
	if (FindColor(pmap, pmap->blue, NUMBLUE(pVisual), &rgb, &pixB, BLUEMAP,
		      -1, BlueComp) != Success)
	    pixB = FindBestPixel(pmap->blue, NUMBLUE(pVisual), &rgb, BLUEMAP)
			<< pVisual->offsetBlue;
	item->pixel = pixR | pixG | pixB;
	break;

    case TrueColor:
	/* Look up each component in its own map, then OR them together */
	pixR = FindBestPixel(pmap->red, NUMRED(pVisual), &rgb, REDMAP);
	pixG = FindBestPixel(pmap->green, NUMGREEN(pVisual), &rgb, GREENMAP);
	pixB = FindBestPixel(pmap->blue, NUMBLUE(pVisual), &rgb, BLUEMAP);
	item->pixel = (pixR << pVisual->offsetRed) |
		      (pixG << pVisual->offsetGreen) |
		      (pixB << pVisual->offsetBlue);
	break;
    }
}

/* free a pixel value obtained from FakeAllocColor */
void
FakeFreeColor(pmap, pixel)
    register ColormapPtr pmap;
    Pixel pixel;
{
    register VisualPtr pVisual;
    Pixel pixR, pixG, pixB;

    switch (pmap->class) {
    case GrayScale:
    case PseudoColor:
	if (pmap->red[pixel].refcnt == AllocTemporary)
	    pmap->red[pixel].refcnt = 0;
	break;
    case DirectColor:
	pVisual = pmap->pVisual;
	pixR = (pixel & pVisual->redMask) >> pVisual->offsetRed; 
	pixG = (pixel & pVisual->greenMask) >> pVisual->offsetGreen; 
	pixB = (pixel & pVisual->blueMask) >> pVisual->offsetBlue; 
	if (pmap->red[pixR].refcnt == AllocTemporary)
	    pmap->red[pixR].refcnt = 0;
	if (pmap->green[pixG].refcnt == AllocTemporary)
	    pmap->green[pixG].refcnt = 0;
	if (pmap->blue[pixB].refcnt == AllocTemporary)
	    pmap->blue[pixB].refcnt = 0;
	break;
    }
}

typedef unsigned short	BigNumUpper;
typedef unsigned long	BigNumLower;

#define BIGNUMLOWERBITS	24
#define BIGNUMUPPERBITS	16
#define BIGNUMLOWER (1 << BIGNUMLOWERBITS)
#define BIGNUMUPPER (1 << BIGNUMUPPERBITS)
#define UPPERPART(i)	((i) >> BIGNUMLOWERBITS)
#define LOWERPART(i)	((i) & (BIGNUMLOWER - 1))

typedef struct _bignum {
    BigNumUpper	upper;
    BigNumLower	lower;
} BigNumRec, *BigNumPtr;

#define BigNumGreater(x,y) (((x)->upper > (y)->upper) ||\
			    ((x)->upper == (y)->upper && (x)->lower > (y)->lower))

#define UnsignedToBigNum(u,r)	(((r)->upper = UPPERPART(u)), \
				 ((r)->lower = LOWERPART(u)))

#define MaxBigNum(r)		(((r)->upper = BIGNUMUPPER-1), \
				 ((r)->lower = BIGNUMLOWER-1))

static void
#if NeedFunctionPrototypes
BigNumAdd (BigNumPtr x, BigNumPtr y, BigNumPtr r)
#else
BigNumAdd (x, y, r)
    BigNumPtr	x, y, r;
#endif
{
    BigNumLower	lower, carry = 0;

    lower = x->lower + y->lower;
    if (lower >= BIGNUMLOWER) {
	lower -= BIGNUMLOWER;
	carry = 1;
    }
    r->lower = lower;
    r->upper = x->upper + y->upper + carry;
}

static Pixel
FindBestPixel(pentFirst, size, prgb, channel)
    EntryPtr	pentFirst;
    int		size;
    xrgb	*prgb;
    int		channel;
{
    EntryPtr	pent;
    Pixel	pixel, final;
    long	dr, dg, db;
    unsigned long   sq;
    BigNumRec	minval, sum, temp;

    final = 0;
    MaxBigNum(&minval);
    /* look for the minimal difference */
    for (pent = pentFirst, pixel = 0; pixel < size; pent++, pixel++)
    {
	dr = dg = db = 0;
	switch(channel)
	{
	  case PSEUDOMAP:
	      dg = (long) pent->co.local.green - prgb->green;
	      db = (long) pent->co.local.blue - prgb->blue;
	  case REDMAP:
	      dr = (long) pent->co.local.red - prgb->red;
	      break;
	  case GREENMAP:
	      dg = (long) pent->co.local.green - prgb->green;
	      break;
	  case BLUEMAP:
	      db = (long) pent->co.local.blue - prgb->blue;
	      break;
	}
	sq = dr * dr;
	UnsignedToBigNum (sq, &sum);
	sq = dg * dg;
	UnsignedToBigNum (sq, &temp);
	BigNumAdd (&sum, &temp, &sum);
	sq = db * db;
	UnsignedToBigNum (sq, &temp);
	BigNumAdd (&sum, &temp, &sum);
	if (BigNumGreater (&minval, &sum))
	{
	    final = pixel;
	    minval = sum;
	}
    }
    return(final);
}

static void
FindColorInRootCmap (pmap, pentFirst, size, prgb, pPixel, channel, comp)
    ColormapPtr	pmap;
    EntryPtr	pentFirst;
    int		size;
    xrgb*	prgb;
    Pixel*	pPixel;
    int		channel;
    ColorCompareProcPtr comp;
{
    EntryPtr    pent;
    Pixel	pixel;
    int         count;

    if ((pixel = *pPixel) >= size)
	pixel = 0;
    for (pent = pentFirst + pixel, count = size; --count >= 0; pent++, pixel++)
    {
	if (pent->refcnt > 0 && (*comp) (pent, prgb))
	{
	    switch (channel)
	    {
	    case REDMAP:
		pixel <<= pmap->pVisual->offsetRed;
		break;
	    case GREENMAP:
		pixel <<= pmap->pVisual->offsetGreen;
		break;
	    case BLUEMAP:
		pixel <<= pmap->pVisual->offsetBlue;
		break;
	    default: /* PSEUDOMAP */
		break;
	    }
	    *pPixel = pixel;
	}
    }
}

/* Tries to find a color in pmap that exactly matches the one requested in prgb
 * if it can't it allocates one.
 * Starts looking at pentFirst + *pPixel, so if you want a specific pixel,
 * load *pPixel with that value, otherwise set it to 0
 */
int
FindColor (pmap, pentFirst, size, prgb, pPixel, channel, client, comp)
    ColormapPtr	pmap;
    EntryPtr	pentFirst;
    int		size;
    xrgb	*prgb;
    Pixel	*pPixel;
    int		channel;
    int		client;
    ColorCompareProcPtr comp;
{
    EntryPtr	pent;
    Bool	foundFree;
    Pixel	pixel, Free = 0;
    int		npix, count, *nump = NULL;
    Pixel	**pixp = NULL, *ppix;
    xColorItem	def;

    foundFree = FALSE;

    if((pixel = *pPixel) >= size)
	pixel = 0;
    /* see if there is a match, and also look for a free entry */
    for (pent = pentFirst + pixel, count = size; --count >= 0; )
    {
        if (pent->refcnt > 0)
	{
    	    if ((*comp) (pent, prgb))
	    {
		if (client >= 0)
		    pent->refcnt++;
		*pPixel = pixel;
		switch(channel)
		{
		  case REDMAP:
		    *pPixel <<= pmap->pVisual->offsetRed;
		  case PSEUDOMAP:
		    break;
		  case GREENMAP:
		    *pPixel <<= pmap->pVisual->offsetGreen;
		    break;
		  case BLUEMAP:
		    *pPixel <<= pmap->pVisual->offsetBlue;
		    break;
		}
		goto gotit;
    	    }
        }
	else if (!foundFree && pent->refcnt == 0)
	{
	    Free = pixel;
	    foundFree = TRUE;
	    /* If we're initializing the colormap, then we are looking for
	     * the first free cell we can find, not to minimize the number
	     * of entries we use.  So don't look any further. */
	    if(pmap->flags & BeingCreated)
		break;
	}
	pixel++;
	if(pixel >= size)
	{
	    pent = pentFirst;
	    pixel = 0;
	}
	else
	    pent++;
    }

    /* If we got here, we didn't find a match.  If we also didn't find
     * a free entry, we're out of luck.  Otherwise, we'll usurp a free
     * entry and fill it in */
    if (!foundFree)
	return (BadAlloc);
    pent = pentFirst + Free;
    pent->fShared = FALSE;
    pent->refcnt = (client >= 0) ? 1 : AllocTemporary;

    switch (channel)
    {
      case PSEUDOMAP:
        pent->co.local.red = prgb->red;
        pent->co.local.green = prgb->green;
        pent->co.local.blue = prgb->blue;
        def.red = prgb->red;
	def.green = prgb->green;
	def.blue = prgb->blue;
	def.flags = (DoRed|DoGreen|DoBlue);
	if (client >= 0)
	    pmap->freeRed--;
	def.pixel = Free;
	break;

      case REDMAP:
        pent->co.local.red = prgb->red;
        def.red = prgb->red;
	def.green = pmap->green[0].co.local.green;
	def.blue = pmap->blue[0].co.local.blue;
	def.flags = DoRed;
	if (client >= 0)
	    pmap->freeRed--;
	def.pixel = Free << pmap->pVisual->offsetRed;
	break;

      case GREENMAP:
	pent->co.local.green = prgb->green;
	def.red = pmap->red[0].co.local.red;
        def.green = prgb->green;
	def.blue = pmap->blue[0].co.local.blue;
	def.flags = DoGreen;
	if (client >= 0)
	    pmap->freeGreen--;
	def.pixel = Free << pmap->pVisual->offsetGreen;
	break;

      case BLUEMAP:
	pent->co.local.blue = prgb->blue;
	def.red = pmap->red[0].co.local.red;
	def.green = pmap->green[0].co.local.green;
	def.blue = prgb->blue;
	def.flags = DoBlue;
	if (client >= 0)
	    pmap->freeBlue--;
	def.pixel = Free << pmap->pVisual->offsetBlue;
	break;
    }
    (*pmap->pScreen->StoreColors) (pmap, 1, &def);
    pixel = Free;	
    *pPixel = def.pixel;

gotit:
    if (pmap->flags & BeingCreated || client == -1)
	return(Success);
    /* Now remember the pixel, for freeing later */
    switch (channel)
    {
      case PSEUDOMAP:
      case REDMAP:
	nump = pmap->numPixelsRed;
	pixp = pmap->clientPixelsRed;
	break;

      case GREENMAP:
	nump = pmap->numPixelsGreen;
	pixp = pmap->clientPixelsGreen;
	break;

      case BLUEMAP:
	nump = pmap->numPixelsBlue;
	pixp = pmap->clientPixelsBlue;
	break;
    }
    npix = nump[client];
    ppix = (Pixel *) xrealloc (pixp[client], (npix + 1) * sizeof(Pixel));
    if (!ppix)
    {
	pent->refcnt--;
	if (!pent->fShared)
	    switch (channel)
	    {
	      case PSEUDOMAP:
	      case REDMAP:
		pmap->freeRed++;
		break;
	      case GREENMAP:
		pmap->freeGreen++;
		break;
	      case BLUEMAP:
		pmap->freeBlue++;
		break;
	    }
	return(BadAlloc);
    }
    ppix[npix] = pixel;
    pixp[client] = ppix;
    nump[client]++;

    return(Success);
}

/* Comparison functions -- passed to FindColor to determine if an
 * entry is already the color we're looking for or not */
static int
AllComp (pent, prgb)
    EntryPtr	pent;
    xrgb	*prgb;
{
    if((pent->co.local.red == prgb->red) &&
       (pent->co.local.green == prgb->green) &&
       (pent->co.local.blue == prgb->blue) )
       return (1);
    return (0);
}

static int
RedComp (pent, prgb)
    EntryPtr	pent;
    xrgb	*prgb;
{
    if (pent->co.local.red == prgb->red) 
	return (1);
    return (0);
}

static int
GreenComp (pent, prgb)
    EntryPtr	pent;
    xrgb	*prgb;
{
    if (pent->co.local.green == prgb->green) 
	return (1);
    return (0);
}

static int
BlueComp (pent, prgb)
    EntryPtr	pent;
    xrgb	*prgb;
{
    if (pent->co.local.blue == prgb->blue) 
	return (1);
    return (0);
}


/* Read the color value of a cell */

int
QueryColors (pmap, count, ppixIn, prgbList)
    ColormapPtr	pmap;
    int		count;
    Pixel	*ppixIn;
    xrgb	*prgbList;
{
    Pixel	*ppix, pixel;
    xrgb	*prgb;
    VisualPtr	pVisual;
    EntryPtr	pent;
    Pixel	i;
    int		errVal = Success;

    pVisual = pmap->pVisual;
    if ((pmap->class | DynamicClass) == DirectColor)
    {
	int numred, numgreen, numblue;
	Pixel rgbbad;

	numred = NUMRED(pVisual);
	numgreen = NUMGREEN(pVisual);
	numblue = NUMBLUE(pVisual);
	rgbbad = ~RGBMASK(pVisual);
	for( ppix = ppixIn, prgb = prgbList; --count >= 0; ppix++, prgb++)
	{
	    pixel = *ppix;
	    if (pixel & rgbbad) {
		clientErrorValue = pixel;
		errVal =  BadValue;
		continue;
	    }
	    i  = (pixel & pVisual->redMask) >> pVisual->offsetRed;
	    if (i >= numred)
	    {
		clientErrorValue = pixel;
		errVal =  BadValue;
		continue;
	    }
	    prgb->red = pmap->red[i].co.local.red;
	    i  = (pixel & pVisual->greenMask) >> pVisual->offsetGreen;
	    if (i >= numgreen)
	    {
		clientErrorValue = pixel;
		errVal =  BadValue;
		continue;
	    }
	    prgb->green = pmap->green[i].co.local.green;
	    i  = (pixel & pVisual->blueMask) >> pVisual->offsetBlue;
	    if (i >= numblue)
	    {
		clientErrorValue = pixel;
		errVal =  BadValue;
		continue;
	    }
	    prgb->blue = pmap->blue[i].co.local.blue;
	}
    }
    else
    {
	for( ppix = ppixIn, prgb = prgbList; --count >= 0; ppix++, prgb++)
	{
	    pixel = *ppix;
	    if (pixel >= pVisual->ColormapEntries)
	    {
		clientErrorValue = pixel;
		errVal = BadValue;
	    }
	    else
	    {
		pent = (EntryPtr)&pmap->red[pixel];
		if (pent->fShared)
		{
		    prgb->red = pent->co.shco.red->color;
		    prgb->green = pent->co.shco.green->color;
		    prgb->blue = pent->co.shco.blue->color;
		}
		else
		{
		    prgb->red = pent->co.local.red;
		    prgb->green = pent->co.local.green;
		    prgb->blue = pent->co.local.blue;
		}
	    }
	}
    }
    return (errVal);
}

static void
FreePixels(pmap, client)
    register ColormapPtr	pmap;
    register int 		client;
{
    register Pixel		*ppix, *ppixStart;
    register int 		n;
    int				class;
#ifdef LBX
    Bool			grabbed;
    Bool			zeroRefCount;
    Bool			anyRefCountReachedZero = 0;
#endif

    class = pmap->class;
    ppixStart = pmap->clientPixelsRed[client];
    if (class & DynamicClass)
    {
	n = pmap->numPixelsRed[client];
#ifdef LBX
	grabbed = LbxCheckCmapGrabbed (pmap);
	if (grabbed)
	{
	    /*
	     * If the colormap is grabbed by a proxy, the server must
	     * notify the proxy of all cells that are freed (the refcount
	     * has reached zero on these cells).
	     */

	    LbxBeginFreeCellsEvent (pmap);
	    LbxSortPixelList (ppixStart, n);
	}
#endif
	for (ppix = ppixStart; --n >= 0; )
	{
	    FreeCell(pmap, *ppix, REDMAP);
#ifdef LBX
	    /*
	     * Only PSEUDO colormaps are grabbed by LBX proxies.
	     * Check if the ref count reached zero on this pixel.
	     */

	    zeroRefCount = pmap->red[*ppix].refcnt == 0;
	    if (zeroRefCount)
		anyRefCountReachedZero = 1;
	    
	    if (grabbed && zeroRefCount)
		LbxAddFreeCellToEvent (pmap, *ppix);
#endif
	    ppix++;
	}
#ifdef LBX
	if (grabbed)
	    LbxEndFreeCellsEvent (pmap);
	else if (anyRefCountReachedZero)
	{
	    /*
	     * We only send LbxFreeCell events to a proxy that has the colormap
	     * grabbed.  If the colormap is not grabbed, the proxy that last
	     * had the colormap grabbed will not be able to do a smart grab
	     * in the future.  A smart grab can only occur if the proxy is kept
	     * up to date on every alloc/free change in the colormap.
	     */

	    LbxDisableSmartGrab (pmap);
	}
#endif
    }

    xfree(ppixStart);
    pmap->clientPixelsRed[client] = (Pixel *) NULL;
    pmap->numPixelsRed[client] = 0;
    if ((class | DynamicClass) == DirectColor) 
    {
        ppixStart = pmap->clientPixelsGreen[client];
	if (class & DynamicClass)
	    for (ppix = ppixStart, n = pmap->numPixelsGreen[client]; --n >= 0;)
		FreeCell(pmap, *ppix++, GREENMAP);
	xfree(ppixStart);
	pmap->clientPixelsGreen[client] = (Pixel *) NULL;
	pmap->numPixelsGreen[client] = 0;

        ppixStart = pmap->clientPixelsBlue[client];
	if (class & DynamicClass)
	    for (ppix = ppixStart, n = pmap->numPixelsBlue[client]; --n >= 0; )
		FreeCell(pmap, *ppix++, BLUEMAP);
	xfree(ppixStart);
	pmap->clientPixelsBlue[client] = (Pixel *) NULL;
	pmap->numPixelsBlue[client] = 0;
    }
}

/* Free all of a client's colors and cells */
/*ARGSUSED*/
int
FreeClientPixels (value, fakeid)
    pointer value;  /* must conform to DeleteType */
    XID	fakeid;
{
    ColormapPtr pmap;
    colorResource *pcr = (colorResource *)value;

    pmap = (ColormapPtr) LookupIDByType(pcr->mid, RT_COLORMAP);
    if (pmap)
	FreePixels(pmap, pcr->client);
    xfree(pcr);
    return Success;
}

int
AllocColorCells (client, pmap, colors, planes, contig, ppix, masks)
    int		client;
    ColormapPtr	pmap;
    int		colors, planes;
    Bool	contig;
    Pixel	*ppix;
    Pixel	*masks;
{
    Pixel	rmask, gmask, bmask, *ppixFirst, r, g, b;
    int		n, class;
    int		ok;
    int		oldcount;
    colorResource *pcr = (colorResource *)NULL;

    class = pmap->class;
    if (!(class & DynamicClass))
	return (BadAlloc); /* Shouldn't try on this type */
    oldcount = pmap->numPixelsRed[client];
    if (pmap->class == DirectColor)
	oldcount += pmap->numPixelsGreen[client] + pmap->numPixelsBlue[client];
    if (!oldcount && (CLIENT_ID(pmap->mid) != client))
    {
	pcr = (colorResource *) xalloc(sizeof(colorResource));
	if (!pcr)
	    return (BadAlloc);
    }

    if (pmap->class == DirectColor)
    {
        ok = AllocDirect (client, pmap, colors, planes, planes, planes,
			  contig, ppix, &rmask, &gmask, &bmask);
	if(ok == Success)
	{
	    for (r = g = b = 1, n = planes; --n >= 0; r += r, g += g, b += b)
	    {
		while(!(rmask & r))
		    r += r;
		while(!(gmask & g))
		    g += g;
		while(!(bmask & b))
		    b += b;
		*masks++ = r | g | b;
	    }
	}
    }
    else
    {
        ok = AllocPseudo (client, pmap, colors, planes, contig, ppix, &rmask,
			  &ppixFirst);
	if(ok == Success)
	{
	    for (r = 1, n = planes; --n >= 0; r += r)
	    {
		while(!(rmask & r))
		    r += r;
		*masks++ = r;
	    }
	}
    }

    /* if this is the client's first pixels in this colormap, tell the
     * resource manager that the client has pixels in this colormap which
     * should be freed when the client dies */
    if ((ok == Success) && pcr)
    {
	pcr->mid = pmap->mid;
	pcr->client = client;
	if (!AddResource(FakeClientID(client), RT_CMAPENTRY, (pointer)pcr))
	    ok = BadAlloc;
    } else if (pcr)
	xfree(pcr);

    return (ok);
}


int
AllocColorPlanes (client, pmap, colors, r, g, b, contig, pixels,
		  prmask, pgmask, pbmask)
    int		client;
    ColormapPtr	pmap;
    int		colors, r, g, b;
    Bool	contig;
    Pixel	*pixels;
    Pixel	*prmask, *pgmask, *pbmask;
{
    int		ok;
    Pixel	mask, *ppixFirst;
    register Pixel shift;
    register int i;
    int		class;
    int		oldcount;
    colorResource *pcr = (colorResource *)NULL;

    class = pmap->class;
    if (!(class & DynamicClass))
	return (BadAlloc); /* Shouldn't try on this type */
    oldcount = pmap->numPixelsRed[client];
    if (class == DirectColor)
	oldcount += pmap->numPixelsGreen[client] + pmap->numPixelsBlue[client];
    if (!oldcount && (CLIENT_ID(pmap->mid) != client))
    {
	pcr = (colorResource *) xalloc(sizeof(colorResource));
	if (!pcr)
	    return (BadAlloc);
    }

    if (class == DirectColor)
    {
        ok = AllocDirect (client, pmap, colors, r, g, b, contig, pixels,
			  prmask, pgmask, pbmask);
    }
    else
    {
	/* Allocate the proper pixels */
	/* XXX This is sort of bad, because of contig is set, we force all
	 * r + g + b bits to be contiguous.  Should only force contiguity
	 * per mask 
	 */
        ok = AllocPseudo (client, pmap, colors, r + g + b, contig, pixels,
			  &mask, &ppixFirst);

	if(ok == Success)
	{
	    /* now split that mask into three */
	    *prmask = *pgmask = *pbmask = 0;
	    shift = 1;
	    for (i = r; --i >= 0; shift += shift)
	    {
		while (!(mask & shift))
		    shift += shift;
		*prmask |= shift;
	    }
	    for (i = g; --i >= 0; shift += shift)
	    {
		while (!(mask & shift))
		    shift += shift;
		*pgmask |= shift;
	    }
	    for (i = b; --i >= 0; shift += shift)
	    {
		while (!(mask & shift))
		    shift += shift;
		*pbmask |= shift;
	    }

	    /* set up the shared color cells */
	    if (!AllocShared(pmap, pixels, colors, r, g, b,
			     *prmask, *pgmask, *pbmask, ppixFirst))
	    {
		(void)FreeColors(pmap, client, colors, pixels, mask);
		ok = BadAlloc;
	    }
	}
    }

    /* if this is the client's first pixels in this colormap, tell the
     * resource manager that the client has pixels in this colormap which
     * should be freed when the client dies */
    if ((ok == Success) && pcr)
    {
	pcr->mid = pmap->mid;
	pcr->client = client;
	if (!AddResource(FakeClientID(client), RT_CMAPENTRY, (pointer)pcr))
	    ok = BadAlloc;
    } else if (pcr)
	xfree(pcr);

    return (ok);
}

static int
AllocDirect (client, pmap, c, r, g, b, contig, pixels, prmask, pgmask, pbmask)
    int		client;
    ColormapPtr	pmap;
    int		c, r, g, b;
    Bool	contig;
    Pixel	*pixels;
    Pixel	*prmask, *pgmask, *pbmask;
{
    Pixel	*ppixRed, *ppixGreen, *ppixBlue;
    Pixel	*ppix, *pDst, *p;
    int		npix, npixR, npixG, npixB;
    Bool	okR, okG, okB;
    Pixel	*rpix = 0, *gpix = 0, *bpix = 0;

    npixR = c << r;
    npixG = c << g;
    npixB = c << b;
    if ((r >= 32) || (g >= 32) || (b >= 32) ||
	(npixR > pmap->freeRed) || (npixR < c) ||
	(npixG > pmap->freeGreen) || (npixG < c) ||
	(npixB > pmap->freeBlue) || (npixB < c))
	return BadAlloc;

    /* start out with empty pixels */
    for(p = pixels; p < pixels + c; p++)
	*p = 0;

    ppixRed = (Pixel *)ALLOCATE_LOCAL(npixR * sizeof(Pixel));
    ppixGreen = (Pixel *)ALLOCATE_LOCAL(npixG * sizeof(Pixel));
    ppixBlue = (Pixel *)ALLOCATE_LOCAL(npixB * sizeof(Pixel));
    if (!ppixRed || !ppixGreen || !ppixBlue)
    {
	if (ppixBlue) DEALLOCATE_LOCAL(ppixBlue);
	if (ppixGreen) DEALLOCATE_LOCAL(ppixGreen);
	if (ppixRed) DEALLOCATE_LOCAL(ppixRed);
	return(BadAlloc);
    }

    okR = AllocCP(pmap, pmap->red, c, r, contig, ppixRed, prmask);
    okG = AllocCP(pmap, pmap->green, c, g, contig, ppixGreen, pgmask);
    okB = AllocCP(pmap, pmap->blue, c, b, contig, ppixBlue, pbmask);

    if (okR && okG && okB)
    {
	rpix = (Pixel *) xrealloc(pmap->clientPixelsRed[client],
				  (pmap->numPixelsRed[client] + (c << r)) *
				  sizeof(Pixel));
	if (rpix)
	    pmap->clientPixelsRed[client] = rpix;
	gpix = (Pixel *) xrealloc(pmap->clientPixelsGreen[client],
				  (pmap->numPixelsGreen[client] + (c << g)) *
				  sizeof(Pixel));
	if (gpix)
	    pmap->clientPixelsGreen[client] = gpix;
	bpix = (Pixel *) xrealloc(pmap->clientPixelsBlue[client],
				  (pmap->numPixelsBlue[client] + (c << b)) *
				  sizeof(Pixel));
	if (bpix)
	    pmap->clientPixelsBlue[client] = bpix;
    }

    if (!okR || !okG || !okB || !rpix || !gpix || !bpix)
    {
	if (okR)
	    for(ppix = ppixRed, npix = npixR; --npix >= 0; ppix++)
		pmap->red[*ppix].refcnt = 0;
	if (okG)
	    for(ppix = ppixGreen, npix = npixG; --npix >= 0; ppix++)
		pmap->green[*ppix].refcnt = 0;
	if (okB)
	    for(ppix = ppixBlue, npix = npixB; --npix >= 0; ppix++)
		pmap->blue[*ppix].refcnt = 0;
	DEALLOCATE_LOCAL(ppixBlue);
	DEALLOCATE_LOCAL(ppixGreen);
	DEALLOCATE_LOCAL(ppixRed);
	return(BadAlloc);
    }

    *prmask <<= pmap->pVisual->offsetRed;
    *pgmask <<= pmap->pVisual->offsetGreen;
    *pbmask <<= pmap->pVisual->offsetBlue;

    ppix = rpix + pmap->numPixelsRed[client];
    for (pDst = pixels, p = ppixRed; p < ppixRed + npixR; p++)
    {
	*ppix++ = *p;
	if(p < ppixRed + c)
	    *pDst++ |= *p << pmap->pVisual->offsetRed;
    }
    pmap->numPixelsRed[client] += npixR;
    pmap->freeRed -= npixR;

    ppix = gpix + pmap->numPixelsGreen[client];
    for (pDst = pixels, p = ppixGreen; p < ppixGreen + npixG; p++)
    {
	*ppix++ = *p;
	if(p < ppixGreen + c)
	    *pDst++ |= *p << pmap->pVisual->offsetGreen;
    }
    pmap->numPixelsGreen[client] += npixG;
    pmap->freeGreen -= npixG;

    ppix = bpix + pmap->numPixelsBlue[client];
    for (pDst = pixels, p = ppixBlue; p < ppixBlue + npixB; p++)
    {
	*ppix++ = *p;
	if(p < ppixBlue + c)
	    *pDst++ |= *p << pmap->pVisual->offsetBlue;
    }
    pmap->numPixelsBlue[client] += npixB;
    pmap->freeBlue -= npixB;

    DEALLOCATE_LOCAL(ppixBlue);
    DEALLOCATE_LOCAL(ppixGreen);
    DEALLOCATE_LOCAL(ppixRed);

    return (Success);
}

static int
AllocPseudo (client, pmap, c, r, contig, pixels, pmask, pppixFirst)
    int		client;
    ColormapPtr	pmap;
    int		c, r;
    Bool	contig;
    Pixel	*pixels;
    Pixel	*pmask;
    Pixel	**pppixFirst;
{
    Pixel	*ppix, *p, *pDst, *ppixTemp;
    int		npix;
    Bool	ok;

    npix = c << r;
    if ((r >= 32) || (npix > pmap->freeRed) || (npix < c))
	return(BadAlloc);
    if(!(ppixTemp = (Pixel *)ALLOCATE_LOCAL(npix * sizeof(Pixel))))
	return(BadAlloc);
    ok = AllocCP(pmap, pmap->red, c, r, contig, ppixTemp, pmask);

    if (ok)
    {

	/* all the allocated pixels are added to the client pixel list,
	 * but only the unique ones are returned to the client */
	ppix = (Pixel *)xrealloc(pmap->clientPixelsRed[client],
			 (pmap->numPixelsRed[client] + npix) * sizeof(Pixel));
	if (!ppix)
	{
	    for (p = ppixTemp; p < ppixTemp + npix; p++)
		pmap->red[*p].refcnt = 0;
	    return (BadAlloc);
	}
	pmap->clientPixelsRed[client] = ppix;
	ppix += pmap->numPixelsRed[client];
	*pppixFirst = ppix;
	pDst = pixels;
	for (p = ppixTemp; p < ppixTemp + npix; p++)
	{
	    *ppix++ = *p;
	    if(p < ppixTemp + c)
	        *pDst++ = *p;
	}
	pmap->numPixelsRed[client] += npix;
	pmap->freeRed -= npix;
    }
    DEALLOCATE_LOCAL(ppixTemp);
    return (ok ? Success : BadAlloc);
}

/* Allocates count << planes pixels from colormap pmap for client. If
 * contig, then the plane mask is made of consecutive bits.  Returns
 * all count << pixels in the array pixels. The first count of those
 * pixels are the unique pixels.  *pMask has the mask to Or with the
 * unique pixels to get the rest of them.
 *
 * Returns True iff all pixels could be allocated 
 * All cells allocated will have refcnt set to AllocPrivate and shared to FALSE
 * (see AllocShared for why we care)
 */
static Bool
AllocCP (pmap, pentFirst, count, planes, contig, pixels, pMask)
    ColormapPtr	pmap;
    EntryPtr	pentFirst;
    int		count, planes;
    Bool	contig;
    Pixel	*pixels, *pMask;
    
{
    EntryPtr	ent;
    Pixel	pixel, base, entries, maxp, save;
    int		dplanes, found;
    Pixel	*ppix;
    Pixel	mask;
    Pixel	finalmask;

    dplanes = pmap->pVisual->nplanes;

    /* Easy case.  Allocate pixels only */
    if (planes == 0)
    {
        /* allocate writable entries */
	ppix = pixels;
        ent = pentFirst;
        pixel = 0;
        while (--count >= 0)
	{
            /* Just find count unallocated cells */
    	    while (ent->refcnt)
	    {
    	        ent++;
    	        pixel++;
    	    }
    	    ent->refcnt = AllocPrivate;
    	    *ppix++ = pixel;
	    ent->fShared = FALSE;
        }
        *pMask = 0;
        return (TRUE);
    }
    else if (planes > dplanes)
    {
	return (FALSE);
    }

    /* General case count pixels * 2 ^ planes cells to be allocated */

    /* make room for new pixels */
    ent = pentFirst;

    /* first try for contiguous planes, since it's fastest */
    for (mask = (((Pixel)1) << planes) - 1, base = 1, dplanes -= (planes - 1);
         --dplanes >= 0;
         mask += mask, base += base)
    {
        ppix = pixels;
        found = 0;
        pixel = 0;
        entries = pmap->pVisual->ColormapEntries - mask;
        while (pixel < entries)
	{
    	    save = pixel;
    	    maxp = pixel + mask + base;
    	    /* check if all are free */
    	    while (pixel != maxp && ent[pixel].refcnt == 0)
    	        pixel += base;
	    if (pixel == maxp)
		{
		    /* this one works */
		    *ppix++ = save;
		    found++;
		    if (found == count)
		    {
			/* found enough, allocate them all */
			while (--count >= 0)
			{
			    pixel = pixels[count];
			    maxp = pixel + mask;
			    while (1)
			    {
				ent[pixel].refcnt = AllocPrivate;
				ent[pixel].fShared = FALSE;
				if (pixel == maxp)
				    break;
				pixel += base;
				*ppix++ = pixel;
			    }
			}
			*pMask = mask;
			return (TRUE);
		    }
		}
    	    pixel = save + 1;
    	    if (pixel & mask)
    	        pixel += mask;
        }
    }

    dplanes = pmap->pVisual->nplanes;
    if (contig || planes == 1 || dplanes < 3)
	return (FALSE);

    /* this will be very slow for large maps, need a better algorithm */

    /*
       we can generate the smallest and largest numbers that fits in dplanes
       bits and contain exactly planes bits set as follows. First, we need to
       check that it is possible to generate such a mask at all.
       (Non-contiguous masks need one more bit than contiguous masks). Then
       the smallest such mask consists of the rightmost planes-1 bits set, then
       a zero, then a one in position planes + 1. The formula is
         (3 << (planes-1)) -1
       The largest such masks consists of the leftmost planes-1 bits set, then
       a zero, then a one bit in position dplanes-planes-1. If dplanes is
       smaller than 32 (the number of bits in a word) then the formula is:
         (1<<dplanes) - (1<<(dplanes-planes+1) + (1<<dplanes-planes-1)
       If dplanes = 32, then we can't calculate (1<<dplanes) and we have
       to use:
         ( (1<<(planes-1)) - 1) << (dplanes-planes+1) + (1<<(dplanes-planes-1))
	  
	  << Thank you, Loretta>>>

    */

    finalmask =
        (((((Pixel)1)<<(planes-1)) - 1) << (dplanes-planes+1)) +
	  (((Pixel)1)<<(dplanes-planes-1));
    for (mask = (((Pixel)3) << (planes -1)) - 1; mask <= finalmask; mask++)
    {
        /* next 3 magic statements count number of ones (HAKMEM #169) */
        pixel = (mask >> 1) & 033333333333;
        pixel = mask - pixel - ((pixel >> 1) & 033333333333);
        if ((((pixel + (pixel >> 3)) & 030707070707) % 077) != planes)
    	    continue;
        ppix = pixels;
        found = 0;
        entries = pmap->pVisual->ColormapEntries - mask;
        base = lowbit (mask);
        for (pixel = 0; pixel < entries; pixel++)
	{
	    if (pixel & mask)
	        continue;
	    maxp = 0;
	    /* check if all are free */
	    while (ent[pixel + maxp].refcnt == 0)
	    {
		GetNextBitsOrBreak(maxp, mask, base);
	    }
	    if ((maxp < mask) || (ent[pixel + mask].refcnt != 0))
		continue;
	    /* this one works */
	    *ppix++ = pixel;
	    found++;
	    if (found < count)
		continue;
	    /* found enough, allocate them all */
	    while (--count >= 0)
	    {
		pixel = (pixels)[count];
		maxp = 0;
		while (1)
		{
		    ent[pixel + maxp].refcnt = AllocPrivate;
		    ent[pixel + maxp].fShared = FALSE;
		    GetNextBitsOrBreak(maxp, mask, base);
		    *ppix++ = pixel + maxp;
		}
	    }

	    *pMask = mask;
	    return (TRUE);
	}
    }
    return (FALSE);
}

static Bool
AllocShared (pmap, ppix, c, r, g, b, rmask, gmask, bmask, ppixFirst)
    ColormapPtr	pmap;
    Pixel	*ppix;
    int		c, r, g, b;
    Pixel	rmask, gmask, bmask;
    Pixel	*ppixFirst;	/* First of the client's new pixels */
{
    Pixel	*pptr, *cptr;
    int		npix, z, npixClientNew, npixShared;
    Pixel	basemask, base, bits, common;
    SHAREDCOLOR *pshared, **ppshared, **psharedList;

    npixClientNew = c << (r + g + b);
    npixShared = (c << r) + (c << g) + (c << b);
    psharedList = (SHAREDCOLOR **)ALLOCATE_LOCAL(npixShared *
						 sizeof(SHAREDCOLOR *));
    if (!psharedList)
	return FALSE;
    ppshared = psharedList;
    for (z = npixShared; --z >= 0; )
    {
	if (!(ppshared[z] = (SHAREDCOLOR *)xalloc(sizeof(SHAREDCOLOR))))
	{
	    for (z++ ; z < npixShared; z++)
		xfree(ppshared[z]);
	    return FALSE;
	}
    }
    for(pptr = ppix, npix = c; --npix >= 0; pptr++)
    {
	basemask = ~(gmask | bmask);
	common = *pptr & basemask;
	if (rmask)
	{
	    bits = 0;
	    base = lowbit (rmask);
	    while(1)
	    {
		pshared = *ppshared++;
		pshared->refcnt = 1 << (g + b);
		for (cptr = ppixFirst, z = npixClientNew; --z >= 0; cptr++)
		{
		    if ((*cptr & basemask) == (common | bits))
		    {
			pmap->red[*cptr].fShared = TRUE;
			pmap->red[*cptr].co.shco.red = pshared;
		    }
		}
		GetNextBitsOrBreak(bits, rmask, base);
	    }
	}
	else
	{
	    pshared = *ppshared++;
	    pshared->refcnt = 1 << (g + b);
	    for (cptr = ppixFirst, z = npixClientNew; --z >= 0; cptr++)
	    {
		if ((*cptr & basemask) == common)
		{
		    pmap->red[*cptr].fShared = TRUE;
		    pmap->red[*cptr].co.shco.red = pshared;
		}
	    }
	}
	basemask = ~(rmask | bmask);
	common = *pptr & basemask;
	if (gmask)
	{
	    bits = 0;
	    base = lowbit (gmask);
	    while(1)
	    {
		pshared = *ppshared++;
		pshared->refcnt = 1 << (r + b);
		for (cptr = ppixFirst, z = npixClientNew; --z >= 0; cptr++)
		{
		    if ((*cptr & basemask) == (common | bits))
		    {
			pmap->red[*cptr].co.shco.green = pshared;
		    }
		}
		GetNextBitsOrBreak(bits, gmask, base);
	    }
	}
	else
	{
	    pshared = *ppshared++;
	    pshared->refcnt = 1 << (g + b);
	    for (cptr = ppixFirst, z = npixClientNew; --z >= 0; cptr++)
	    {
		if ((*cptr & basemask) == common)
		{
		    pmap->red[*cptr].co.shco.green = pshared;
		}
	    }
	}
	basemask = ~(rmask | gmask);
	common = *pptr & basemask;
	if (bmask)
	{
	    bits = 0;
	    base = lowbit (bmask);
	    while(1)
	    {
		pshared = *ppshared++;
		pshared->refcnt = 1 << (r + g);
		for (cptr = ppixFirst, z = npixClientNew; --z >= 0; cptr++)
		{
		    if ((*cptr & basemask) == (common | bits))
		    {
			pmap->red[*cptr].co.shco.blue = pshared;
		    }
		}
		GetNextBitsOrBreak(bits, bmask, base);
	    }
	}
	else
	{
	    pshared = *ppshared++;
	    pshared->refcnt = 1 << (g + b);
	    for (cptr = ppixFirst, z = npixClientNew; --z >= 0; cptr++)
	    {
		if ((*cptr & basemask) == common)
		{
		    pmap->red[*cptr].co.shco.blue = pshared;
		}
	    }
	}
    }
    DEALLOCATE_LOCAL(psharedList);
    return TRUE;
}


/* Free colors and/or cells (probably slow for large numbers) */

int
FreeColors (pmap, client, count, pixels, mask)
    ColormapPtr	pmap;
    int		client, count;
    Pixel	*pixels;
    Pixel	mask;
{
    int		rval, result, class;
    Pixel	rmask;

    class = pmap->class;
    if (pmap->flags & AllAllocated)
	return(BadAccess);
    if ((class | DynamicClass) == DirectColor)
    {
	rmask = mask & RGBMASK(pmap->pVisual);
        result = FreeCo(pmap, client, REDMAP, count, pixels,
			mask & pmap->pVisual->redMask);
	/* If any of the three calls fails, we must report that, if more
	 * than one fails, it's ok that we report the last one */
        rval = FreeCo(pmap, client, GREENMAP, count, pixels,
		      mask & pmap->pVisual->greenMask);
	if(rval != Success)
	    result = rval;
	rval = FreeCo(pmap, client, BLUEMAP, count, pixels,
		      mask & pmap->pVisual->blueMask);
	if(rval != Success)
	    result = rval;
    }
    else
    {
	rmask = mask & ((((Pixel)1) << pmap->pVisual->nplanes) - 1);
        result = FreeCo(pmap, client, PSEUDOMAP, count, pixels, rmask);
    }
    if ((mask != rmask) && count)
    {
	clientErrorValue = *pixels | mask;
	result = BadValue;
    }
    /* XXX should worry about removing any RT_CMAPENTRY resource */
    return (result);
}

/* Helper for FreeColors -- frees all combinations of *newpixels and mask bits
 * which the client has allocated in channel colormap cells of pmap.
 * doesn't change newpixels if it doesn't need to */
static int
FreeCo (pmap, client, color, npixIn, ppixIn, mask)
    ColormapPtr	pmap;		/* which colormap head */
    int		client;		
    int		color;		/* which sub-map, eg RED, BLUE, PSEUDO */
    int		npixIn;		/* number of pixels passed in */
    Pixel	*ppixIn;	/* list of base pixels */
    Pixel	mask;		/* mask client gave us */ 
{

    Pixel	*ppixClient, pixTest;
    int		npixClient, npixNew, npix;
    Pixel	bits, base, cmask, rgbbad;
    Pixel	*pptr, *cptr;
    int 	n, zapped;
    int		errVal = Success;
    int		offset, numents;
#ifdef LBX
    Bool	grabbed;
    Bool	zeroRefCount;
    Bool	anyRefCountReachedZero = 0;
#endif

    if (npixIn == 0)
        return (errVal);
    bits = 0;
    zapped = 0;
    base = lowbit (mask);

    switch(color)
    {
      case REDMAP:
	cmask = pmap->pVisual->redMask;
	rgbbad = ~RGBMASK(pmap->pVisual);
	offset = pmap->pVisual->offsetRed;
	numents = (cmask >> offset) + 1;
	ppixClient = pmap->clientPixelsRed[client];
	npixClient = pmap->numPixelsRed[client];
	break;
      case GREENMAP:
	cmask = pmap->pVisual->greenMask;
	rgbbad = ~RGBMASK(pmap->pVisual);
	offset = pmap->pVisual->offsetGreen;
	numents = (cmask >> offset) + 1;
	ppixClient = pmap->clientPixelsGreen[client];
	npixClient = pmap->numPixelsGreen[client];
	break;
      case BLUEMAP:
	cmask = pmap->pVisual->blueMask;
	rgbbad = ~RGBMASK(pmap->pVisual);
	offset = pmap->pVisual->offsetBlue;
	numents = (cmask >> offset) + 1;
	ppixClient = pmap->clientPixelsBlue[client];
	npixClient = pmap->numPixelsBlue[client];
	break;
      default:	/* so compiler can see that everything gets initialized */
      case PSEUDOMAP:
	cmask = ~((Pixel)0);
	rgbbad = 0;
	offset = 0;
	numents = pmap->pVisual->ColormapEntries;
	ppixClient = pmap->clientPixelsRed[client];
	npixClient = pmap->numPixelsRed[client];
	break;
    }

#ifdef LBX
    grabbed = LbxCheckCmapGrabbed (pmap);

    if (grabbed)
    {
	/*
	 * If the colormap is grabbed by a proxy, the server must
	 * notify the proxy of all cells that are freed (the refcount
	 * has reached zero on these cells).
	 */

	LbxBeginFreeCellsEvent (pmap);
	LbxSortPixelList (ppixIn, npixIn);
    }
#endif

    /* zap all pixels which match */
    while (1)
    {
        /* go through pixel list */
        for (pptr = ppixIn, n = npixIn; --n >= 0; pptr++)
	{
	    pixTest = ((*pptr | bits) & cmask) >> offset;
	    if ((pixTest >= numents) || (*pptr & rgbbad))
	    {
		clientErrorValue = *pptr | bits;
		errVal = BadValue;
		continue;
	    }

	    /* find match in client list */
	    for (cptr = ppixClient, npix = npixClient;
	         --npix >= 0 && *cptr != pixTest;
		 cptr++) ;

	    if (npix >= 0)
	    {
		if (pmap->class & DynamicClass)
		{
		    FreeCell(pmap, pixTest, color);
#ifdef LBX
		    /*
		     * Only PSEUDO colormaps are grabbed by LBX proxies.
		     * Check if the ref count reached zero on this pixel.
		     */

		    zeroRefCount = pmap->red[pixTest].refcnt == 0;
		    if (zeroRefCount)
			anyRefCountReachedZero = 1;

		    if (grabbed && zeroRefCount)
			LbxAddFreeCellToEvent (pmap, pixTest);
#endif
		}
		*cptr = ~((Pixel)0);
		zapped++;
	    }
	    else
		errVal = BadAccess;
	}
        /* generate next bits value */
	GetNextBitsOrBreak(bits, mask, base);
    }

#ifdef LBX
    if (grabbed)
	LbxEndFreeCellsEvent (pmap);
    else if (anyRefCountReachedZero)
    {
	/*
	 * We only send LbxFreeCell events to a proxy that has the colormap
	 * grabbed.  If the colormap is not grabbed, the proxy that last
	 * had the colormap grabbed will not be able to do a smart grab
	 * in the future.  A smart grab can only occur if the proxy is kept
	 * up to date on every alloc/free change in the colormap.
	 */
	
	LbxDisableSmartGrab (pmap);
    }
#endif

    /* delete freed pixels from client pixel list */
    if (zapped)
    {
        npixNew = npixClient - zapped;
        if (npixNew)
	{
	    /* Since the list can only get smaller, we can do a copy in
	     * place and then realloc to a smaller size */
    	    pptr = cptr = ppixClient;

	    /* If we have all the new pixels, we don't have to examine the
	     * rest of the old ones */
	    for(npix = 0; npix < npixNew; cptr++)
	    {
    	        if (*cptr != ~((Pixel)0))
		{
    		    *pptr++ = *cptr;
		    npix++;
    	        }
    	    }
	    pptr = (Pixel *)xrealloc(ppixClient, npixNew * sizeof(Pixel));
	    if (pptr)
		ppixClient = pptr;
	    npixClient = npixNew;
        }
	else
	{
	    npixClient = 0;
	    xfree(ppixClient);
    	    ppixClient = (Pixel *)NULL;
	}
	switch(color)
	{
	  case PSEUDOMAP:
	  case REDMAP:
	    pmap->clientPixelsRed[client] = ppixClient;
	    pmap->numPixelsRed[client] = npixClient;
	    break;
	  case GREENMAP:
	    pmap->clientPixelsGreen[client] = ppixClient;
	    pmap->numPixelsGreen[client] = npixClient;
	    break;
	  case BLUEMAP:
	    pmap->clientPixelsBlue[client] = ppixClient;
	    pmap->numPixelsBlue[client] = npixClient;
	    break;
	}
    }
    return (errVal);
}



/* Redefine color values */
int
StoreColors (pmap, count, defs)
    ColormapPtr	pmap;
    int		count;
    xColorItem	*defs;
{
    register Pixel 	pix;
    register xColorItem *pdef;
    register EntryPtr 	pent, pentT, pentLast;
    register VisualPtr	pVisual;
    SHAREDCOLOR		*pred, *pgreen, *pblue;
    int			n, ChgRed, ChgGreen, ChgBlue, idef;
    int			class, errVal = Success;
    int			ok;


    class = pmap->class;
    if(!(class & DynamicClass) && !(pmap->flags & BeingCreated))
    {
	return(BadAccess);
    }
    pVisual = pmap->pVisual;

    idef = 0;
    if((class | DynamicClass) == DirectColor)
    {
	int numred, numgreen, numblue;
	Pixel rgbbad;

	numred = NUMRED(pVisual);
	numgreen = NUMGREEN(pVisual);
	numblue = NUMBLUE(pVisual);
	rgbbad = ~RGBMASK(pVisual);
        for (pdef = defs, n = 0; n < count; pdef++, n++)
	{
	    ok = TRUE;
            (*pmap->pScreen->ResolveColor)
	        (&pdef->red, &pdef->green, &pdef->blue, pmap->pVisual);

	    if (pdef->pixel & rgbbad)
	    {
		errVal = BadValue;
		clientErrorValue = pdef->pixel;
		continue;
	    }
	    pix = (pdef->pixel & pVisual->redMask) >> pVisual->offsetRed;
	    if (pix >= numred)
	    {
		errVal = BadValue;
		ok = FALSE;
	    }
	    else if (pmap->red[pix].refcnt != AllocPrivate)
	    {
		errVal = BadAccess;
		ok = FALSE;
	    }
	    else if (pdef->flags & DoRed)
	    {
		pmap->red[pix].co.local.red = pdef->red;
	    }
	    else
	    {
		pdef->red = pmap->red[pix].co.local.red;
	    }

	    pix = (pdef->pixel & pVisual->greenMask) >> pVisual->offsetGreen;
	    if (pix >= numgreen)
	    {
		errVal = BadValue;
		ok = FALSE;
	    }
	    else if (pmap->green[pix].refcnt != AllocPrivate)
	    {
		errVal = BadAccess;
		ok = FALSE;
	    }
	    else if (pdef->flags & DoGreen)
	    {
		pmap->green[pix].co.local.green = pdef->green;
	    }
	    else
	    {
		pdef->green = pmap->green[pix].co.local.green;
	    }

	    pix = (pdef->pixel & pVisual->blueMask) >> pVisual->offsetBlue;
	    if (pix >= numblue)
	    {
		errVal = BadValue;
		ok = FALSE;
	    }
	    else if (pmap->blue[pix].refcnt != AllocPrivate)
	    {
		errVal = BadAccess;
		ok = FALSE;
	    }
	    else if (pdef->flags & DoBlue)
	    {
		pmap->blue[pix].co.local.blue = pdef->blue;
	    }
	    else
	    {
		pdef->blue = pmap->blue[pix].co.local.blue;
	    }
	    /* If this is an o.k. entry, then it gets added to the list
	     * to be sent to the hardware.  If not, skip it.  Once we've
	     * skipped one, we have to copy all the others.
	     */
	    if(ok)
	    {
		if(idef != n)
		    defs[idef] = defs[n];
		idef++;
	    } else
		clientErrorValue = pdef->pixel;
	}
    }
    else
    {
        for (pdef = defs, n = 0; n < count; pdef++, n++)
	{

	    ok = TRUE;
	    if (pdef->pixel >= pVisual->ColormapEntries)
	    {
		clientErrorValue = pdef->pixel;
	        errVal = BadValue;
		ok = FALSE;
	    }
	    else if (pmap->red[pdef->pixel].refcnt != AllocPrivate)
	    {
		errVal = BadAccess;
		ok = FALSE;
	    }

	    /* If this is an o.k. entry, then it gets added to the list
	     * to be sent to the hardware.  If not, skip it.  Once we've
	     * skipped one, we have to copy all the others.
	     */
	    if(ok)
	    {
		if(idef != n)
		    defs[idef] = defs[n];
		idef++;
	    }
	    else
		continue;

            (*pmap->pScreen->ResolveColor)
	        (&pdef->red, &pdef->green, &pdef->blue, pmap->pVisual);

	    pent = &pmap->red[pdef->pixel];

	    if(pdef->flags & DoRed)
	    {
		if(pent->fShared)
		{
		    pent->co.shco.red->color = pdef->red;
		    if (pent->co.shco.red->refcnt > 1)
			ok = FALSE;
		}
		else
		    pent->co.local.red = pdef->red;
	    }
	    else
	    {
		if(pent->fShared)
		    pdef->red = pent->co.shco.red->color;
		else
		    pdef->red = pent->co.local.red;
	    }
	    if(pdef->flags & DoGreen)
	    {
		if(pent->fShared)
		{
		    pent->co.shco.green->color = pdef->green;
		    if (pent->co.shco.green->refcnt > 1)
			ok = FALSE;
		}
		else
		    pent->co.local.green = pdef->green;
	    }
	    else
	    {
		if(pent->fShared)
		    pdef->green = pent->co.shco.green->color;
		else
		    pdef->green = pent->co.local.green;
	    }
	    if(pdef->flags & DoBlue)
	    {
		if(pent->fShared)
		{
		    pent->co.shco.blue->color = pdef->blue;
		    if (pent->co.shco.blue->refcnt > 1)
			ok = FALSE;
		}
		else
		    pent->co.local.blue = pdef->blue;
	    }
	    else
	    {
		if(pent->fShared)
		    pdef->blue = pent->co.shco.blue->color;
		else
		    pdef->blue = pent->co.local.blue;
	    }

	    if(!ok)
	    {
                /* have to run through the colormap and change anybody who
		 * shares this value */
	        pred = pent->co.shco.red;
	        pgreen = pent->co.shco.green;
	        pblue = pent->co.shco.blue;
	        ChgRed = pdef->flags & DoRed;
	        ChgGreen = pdef->flags & DoGreen;
	        ChgBlue = pdef->flags & DoBlue;
	        pentLast = pmap->red + pVisual->ColormapEntries;

	        for(pentT = pmap->red; pentT < pentLast; pentT++)
		{
		    if(pentT->fShared && (pentT != pent))
		    {
			xColorItem	defChg;

			/* There are, alas, devices in this world too dumb
			 * to read their own hardware colormaps.  Sick, but
			 * true.  So we're going to be really nice and load
			 * the xColorItem with the proper value for all the
			 * fields.  We will only set the flags for those
			 * fields that actually change.  Smart devices can
			 * arrange to change only those fields.  Dumb devices
			 * can rest assured that we have provided for them,
			 * and can change all three fields */

			defChg.flags = 0;
			if(ChgRed && pentT->co.shco.red == pred)
			{
			    defChg.flags |= DoRed;
			}
			if(ChgGreen && pentT->co.shco.green == pgreen)
			{
			    defChg.flags |= DoGreen;
			}
			if(ChgBlue && pentT->co.shco.blue == pblue)
			{
			    defChg.flags |= DoBlue;
			}
			if(defChg.flags != 0)
			{
			    defChg.pixel = pentT - pmap->red;
			    defChg.red = pentT->co.shco.red->color;
			    defChg.green = pentT->co.shco.green->color;
			    defChg.blue = pentT->co.shco.blue->color;
			    (*pmap->pScreen->StoreColors) (pmap, 1, &defChg);
			}
		    }
		}

	    }
	}
    }
    /* Note that we use idef, the count of acceptable entries, and not
     * count, the count of proposed entries */
    if (idef != 0)
	( *pmap->pScreen->StoreColors) (pmap, idef, defs);
    return (errVal);
}

int
IsMapInstalled(map, pWin)
    Colormap	map;
    WindowPtr	pWin;
{
    Colormap	*pmaps;
    int		imap, nummaps, found;

    pmaps = (Colormap *) ALLOCATE_LOCAL( 
             pWin->drawable.pScreen->maxInstalledCmaps * sizeof(Colormap));
    if(!pmaps)
	return(FALSE);
    nummaps = (*pWin->drawable.pScreen->ListInstalledColormaps)
        (pWin->drawable.pScreen, pmaps);
    found = FALSE;
    for(imap = 0; imap < nummaps; imap++)
    {
	if(pmaps[imap] == map)
	{
	    found = TRUE;
	    break;
	}
    }
    DEALLOCATE_LOCAL(pmaps);
    return (found);
}
